/*******************************************************************************
 * @file Logger.h
 * Definition of classes for Logging
 * 
 * @package Logging.
 * 
 * @author mk
 * @date 28/12/2008 13:50:21
 * @version 0.1
 ******************************************************************************/

#ifndef __LOGGER_H_INCLUDED__
#define __LOGGER_H_INCLUDED__

#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread.hpp>

namespace Logging 
{
	class Utils
	{
	public:
		static boost::thread::id GetCurrentThreadId();
	};

	class MLogable
	{
	public:
		virtual std::string ToString() const = 0;
	};

	class CLogFile : public std::ofstream
	{
	public:
		CLogFile(const std::string& path) : std::ofstream(path.c_str()) {}
	};

	class CLogNirvana : public std::ostream
	{
	public:
		CLogNirvana() : std::ostream(0) {}
	};

	class CLocation : public MLogable
	{
	public:
		CLocation(char* file, unsigned int line) : file_(file), line_(line) {}
		std::string ToString() const
		{
			std::ostringstream os;
			os << file_ << " (" << line_ << ") ";
			return os.str();
		}

	private:
		std::string file_;
		unsigned int line_;
	};

	class CIndexDateTime : public MLogable
	{
	public:		
		CIndexDateTime() : now_(boost::posix_time::microsec_clock::local_time()) {}
		std::string ToString() const
		{
			return boost::posix_time::to_simple_string(now_);
		}

	private:
		boost::posix_time::ptime now_;
	};

	class CLogger
	{
	public:
		CLogger() : sink_("./Log.txt") {}
		CLogger(const std::string& path) : sink_(path) {}
		std::ostream& Get() { return sink_; }
		boost::mutex& Mutex() { return mtx_; }

	private:
		CLogFile sink_;		
		boost::mutex mtx_;
	};

	class Logger
	{
	public:
		static void Setup()
		{
			if (!logger_)
			{
				logger_ = new CLogger();
			}
		}

		static void Setup(const std::string& aPath)
		{
			if (!logger_)
			{
				logger_ = new CLogger(aPath);
			}
		}

		static void Close()
		{
			if (logger_)
			{
				delete logger_;
			}
			logger_ = NULL;
		}

		static std::ostream& Get();

	private:
		static CLogger* logger_;
	};

	typedef std::vector<std::string> LogFilter;

	class CFilteredLogger : public CLogger
	{
	public:
		CFilteredLogger() : CLogger("./Log.txt") {}		
		CFilteredLogger(const std::string& path, const LogFilter& filter);
		std::ostream& Get(const std::string& level);

	private:
		LogFilter filter_;
		CLogNirvana nirvana_;
	};

	class FilteredLogger
	{
	public:
		static void Setup(const std::string& path, const LogFilter& filter)
		{
			if (!logger_)
			{
				logger_ = new CFilteredLogger(path, filter);
			}
		}

		static void Close()
		{
			if (logger_)
			{
				delete logger_;
			}
			logger_ = NULL;
		}

		static std::ostream& Get(const std::string& level);
		static boost::mutex& Mutex();

	private:
		static CFilteredLogger* logger_;
	};
}

#endif /* __LOGGER_H_INCLUDED__ */